/*
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_base.h"
#include "los_plimits.h"
#include "los_generallimit_pri.h"
#include "los_processlimit_pri.h"
#include "los_memlimit_pri.h"
#include "los_schedlimit_pri.h"
#include "los_ipclimit_pri.h"
#include "los_devicelimit_pri.h"
#include "los_process_pri.h"
#include "hal_timer.h"

LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_plimitsSpin);

BOOL g_isInitProcLimiterSet = FALSE;

UINT64 g_plimitsTickTime = 0;

bool g_isNeedMigrate = false;


static ProcLimiter g_procGeneralLimiterHeader = {
    .id = PROCESS_LIMITER_GENERAL,
    .private = (void *)&g_procGeneralLimiter,
    .parent = NULL,
};

static ProcLimiter g_procPidLimiterHeader = {
    .id = PROCESS_LIMITER_ID_PIDS,
    .private = (void *)&g_procPidLimiter,
    .parent = NULL,
};

static ProcLimiter g_procMemLimiterHeader = {
    .id = PROCESS_LIMITER_ID_MEM,
    .private = (void *)&g_procMemLimiter,
    .parent = NULL,
};

static ProcLimiter g_procSchedLimiterHeader = {
    .id = PROCESS_LIMITER_ID_SCHED,
    .private = (void *)&g_procSchedLimiter,
    .parent = NULL,
};

static ProcLimiter g_procIPCLimiterHeader = {
    .id = PROCESS_LIMITER_ID_IPC,
    .private = (void *)&g_procIPCLimiter,
    .parent = NULL,
};

static ProcLimiter g_procDeviceLimiterHeader = {
    .id = PROCESS_LIMITER_ID_DEV,
    .private = (void *)&g_procDevLimiter,
    .parent = NULL,
};

typedef struct PlimiterOperations {
    void (*LimiterInit)(void);
    void *(*LimiterAlloc)(void);
    void (*LimiterCopy)(UINT8 *, UINT8 *);
    void (*LimiterMigrate)(void);
} PlimiterOperations;

static PlimiterOperations g_limiterOps[PROCESS_LIMITER_COUNT] = {
    [PROCESS_LIMITER_GENERAL] = {
        .LimiterInit = GeneralLimiterInit,
        .LimiterAlloc = GeneralLimiterAlloc,
        .LimiterCopy = GeneralLimiterCopy,
    },
    [PROCESS_LIMITER_ID_PIDS] = {
        .LimiterInit = PidLimiterInit,
        .LimiterAlloc = PidLimiterAlloc,
        .LimiterCopy = PidLimiterCopy,
    },
    [PROCESS_LIMITER_ID_MEM] = {
        .LimiterInit = OsMemLimiterInit,
        .LimiterAlloc = OsMemLimiterAlloc,
        .LimiterCopy = OsMemLimiterCopy,
    },
    [PROCESS_LIMITER_ID_SCHED] = {
        .LimiterInit = SchedLimiterInit,
        .LimiterAlloc = SchedLimiterAlloc,
        .LimiterCopy = SchedLimiterCopy,
        .LimiterMigrate = OsMemLimiterMigrate,
    },
    [PROCESS_LIMITER_ID_IPC] = {
        .LimiterInit = IPCLimiterInit,
        .LimiterAlloc = IPCLimiterAlloc,
        .LimiterCopy = IPCLimiterCopy,
        .LimiterMigrate = IPCLimiterMigrate,
    },
    [PROCESS_LIMITER_ID_DEV] = {
        .LimiterInit = DevLimiterInit,
        .LimiterAlloc = DevLimiterAlloc,
        .LimiterCopy = DevLimiterCopy,
    },
};

ProcLimiterSet g_procLimiterSet;
ProcLimiterSet *g_procLimiterSetArray[LOSCFG_BASE_CORE_PROCESS_LIMIT] = { NULL };

void OsProcLimiterSetInit(void)
{
    LOS_ListInit(&g_procLimiterSet.childList);
    g_procLimiterSet.level = 0;
    g_procLimiterSet.parent = NULL;
    g_procLimiterSet.procLimiterList[g_procGeneralLimiterHeader.id] = &g_procGeneralLimiterHeader;
    g_procLimiterSet.procLimiterList[g_procPidLimiterHeader.id] = &g_procPidLimiterHeader;
    g_procLimiterSet.procLimiterList[g_procMemLimiterHeader.id] = &g_procMemLimiterHeader;
    g_procLimiterSet.procLimiterList[g_procSchedLimiterHeader.id] = &g_procSchedLimiterHeader;
    g_procLimiterSet.procLimiterList[g_procIPCLimiterHeader.id] = &g_procIPCLimiterHeader;
    g_procLimiterSet.procLimiterList[g_procDeviceLimiterHeader.id] = &g_procDeviceLimiterHeader;
    return;
}

void OsPlimitsUpdateTime(void)
{
    g_plimitsTickTime = HalClockGetCycles();
    return;
}

void ProcLimiterSetRootInit(void)
{
    for (int limiterId = 0; limiterId < PROCESS_LIMITER_COUNT; ++limiterId) {
        if (g_limiterOps[limiterId].LimiterInit != NULL) {
            g_limiterOps[limiterId].LimiterInit();
        }
    }
}

void UpdateMigrateStatus(bool status)
{
    UINT32 intSave;
    PLIMITS_LOCK(intSave);
    g_isNeedMigrate = status;
    PLIMITS_UNLOCK(intSave);
    return;
}

void OsProcLimiterSetMigrate(void)
{
    if (g_isNeedMigrate == false) {
        return;
    }
    UINT32 intSave;
    PLIMITS_LOCK(intSave);
    for (int limiterId = 0; limiterId < PROCESS_LIMITER_COUNT; ++limiterId) {
        if (g_limiterOps[limiterId].LimiterMigrate != NULL) {
            g_limiterOps[limiterId].LimiterMigrate();
        }
    }
    g_isNeedMigrate = false;
    PLIMITS_UNLOCK(intSave);
    return;
}

void ProcLimiterFree(ProcLimiter *procLimiter)
{
    if (procLimiter == NULL) {
        return;
    }
    if (procLimiter->private != NULL) {
        LOS_KernelFree(procLimiter->private);
        procLimiter->private = NULL;
    }
    LOS_KernelFree(procLimiter);
    procLimiter = NULL;
    return;
}

void ProcLimiterSetFree(ProcLimiterSet *procLimiterSet)
{
    if (procLimiterSet == NULL) {
        return;
    }
    LOS_ListDelete(&procLimiterSet->childList);
    for (int limiterId = 0; limiterId < PROCESS_LIMITER_COUNT; ++limiterId) {
        ProcLimiterFree(procLimiterSet->procLimiterList[limiterId]);
    }
    LOS_KernelFree(procLimiterSet);
    return;
}

ProcLimiterSet *ProcLimiterSetCreateFromParent(ProcLimiterSet *parentProcLimiterSet)
{
    ProcLimiterSet *procLimiterSet = NULL;
    procLimiterSet = (ProcLimiterSet *)LOS_KernelMalloc(sizeof(ProcLimiterSet));
    if (procLimiterSet == NULL) {
        PRINT_ERR("%s() %d line, procLimiterSet malloc failed.\n", __FUNCTION__, __LINE__);
        return NULL;
    }
    LOS_ListInit(&procLimiterSet->childList);
    procLimiterSet->parent = parentProcLimiterSet;
    procLimiterSet->level = parentProcLimiterSet->level + 1;
    procLimiterSet->mask = parentProcLimiterSet->mask;
    for (int plimiterId = 0; plimiterId < PROCESS_LIMITER_COUNT; ++plimiterId) {
        procLimiterSet->procLimiterList[plimiterId] = (ProcLimiter *)LOS_KernelMalloc(sizeof(ProcLimiter));
        if (procLimiterSet->procLimiterList[plimiterId] == NULL) {
            ProcLimiterSetFree(procLimiterSet);
            procLimiterSet = NULL;
            return NULL;
        }
        memset_s(procLimiterSet->procLimiterList[plimiterId], sizeof(ProcLimiter), 0, sizeof(ProcLimiter));
        procLimiterSet->procLimiterList[plimiterId]->id = parentProcLimiterSet->procLimiterList[plimiterId]->id;
        procLimiterSet->procLimiterList[plimiterId]->parent = parentProcLimiterSet->procLimiterList[plimiterId];
        if ((parentProcLimiterSet->mask) & BIT(plimiterId)) {
            procLimiterSet->procLimiterList[plimiterId]->private = g_limiterOps[plimiterId].LimiterAlloc();
            if (procLimiterSet->procLimiterList[plimiterId]->private == NULL) {
                ProcLimiterSetFree(procLimiterSet);
                procLimiterSet = NULL;
                return NULL;
            }
            g_limiterOps[plimiterId].LimiterCopy((UINT8 *)procLimiterSet->procLimiterList[plimiterId]->private,
                                                 (UINT8 *)parentProcLimiterSet->procLimiterList[plimiterId]->private);
        }
    }
    LOS_ListAdd(&parentProcLimiterSet->childList, &procLimiterSet->childList);
    return procLimiterSet;
}

int ProcLimiterSetAddLimiter(ProcLimiterSet *currProcLimiterSet, enum ProcLimiterId plimiterId)
{
    ProcLimiterSet *parentProcLimiterSet = currProcLimiterSet->parent;

    currProcLimiterSet->procLimiterList[plimiterId]->private = g_limiterOps[plimiterId].LimiterAlloc();
    if (currProcLimiterSet->procLimiterList[plimiterId]->private == NULL) {
        return LOS_NOK;
    }
    g_limiterOps[plimiterId].LimiterCopy(currProcLimiterSet->procLimiterList[plimiterId]->private,
                                         parentProcLimiterSet->procLimiterList[plimiterId]->private);
    return LOS_OK;
}

void ProcLimiterSetReleaseLimiter(ProcLimiterSet *currProcLimiterSet, enum ProcLimiterId plimiterId)
{
    ProcLimiter *procLimiter = currProcLimiterSet->procLimiterList[plimiterId];
    if (procLimiter->private == NULL) {
        return;
    }
    LOS_KernelFree(procLimiter->private);
    procLimiter->private = NULL;
    return;
}

void OsProcessLinkProcLimiterSet(LosProcessCB *processCB)
{
    if (processCB == NULL) {
        PRINT_ERR("processCB is NULL.\n");
        return;
    }
    if (processCB->parentProcessID == OS_INVALID_VALUE) {
        g_procLimiterSetArray[processCB->processID] = &g_procLimiterSet;
    } else {
        g_procLimiterSetArray[processCB->processID] = g_procLimiterSetArray[processCB->parentProcessID];
    }
    return;
}
